package com.tianji.learning.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.tianji.common.utils.DateUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.constans.RedisConstans;
import com.tianji.learning.domian.vo.PointsStatisticsVO;
import com.tianji.learning.entity.PointsRecord;
import com.tianji.learning.enums.PointsRecordType;
import com.tianji.learning.mapper.PointsRecordMapper;
import com.tianji.learning.service.IPointsRecordService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.lang.reflect.Array;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import static com.tianji.common.utils.DateUtils.BOARDS_DATE_SUFFIX_FORMATTER;

/**
 * <p>
 * 学习积分记录，每个月底清零 服务实现类
 * </p>
 *
 * @author author
 * @since 2024-03-24
 */
@Service
@RequiredArgsConstructor
public class PointsRecordServiceImpl extends ServiceImpl<PointsRecordMapper, PointsRecord> implements IPointsRecordService {

    private final StringRedisTemplate redisTemplate;

    /*添加积分记录*/
    public void addPointsRecord(Long userId, int points, PointsRecordType type) {
        //1.判断当前获取积分的方式有没有上限
        int maxPoints = type.getMaxPoints();
        LocalDateTime now = LocalDateTime.now();

        if (maxPoints > 0){
            //2.有，则需要判断是否超过每日上限
            LocalDateTime end = DateUtils.getDayEndTime(now);
            LocalDateTime begin = DateUtils.getDayStartTime(now);

            //2.1.查询今日已得积分
            int currentDayPoints = queryUserPointsByTypeAndDate(userId, type, begin, end);

            //2.2.判断是否超过上限
            if (currentDayPoints >= maxPoints){
                //2.3.超过，直接结束
                return;
            }

            //2.4.处理已有积分 + 本次上限的情况 ： 不能直接加了 ，而是要加上上限 - 已有积分的差值部分
            if (currentDayPoints + points > maxPoints){
                points = maxPoints - currentDayPoints;
            }
        }

        //3.没有超过，直接保存积分记录
        PointsRecord pr = new PointsRecord();
        pr.setPoints(points);
        pr.setType(type);
        pr.setUserId(userId);
        save(pr);

        //4.更新redis
        //4.1.获取zset的key
        String key = RedisConstans.POINTS_BOARD_KEY_PREFIX + now.format(BOARDS_DATE_SUFFIX_FORMATTER);

        //4.2.累加积分
        redisTemplate.opsForZSet().incrementScore(key, userId.toString(), points);
    }

    /*查询我的当天积分*/
    @Override
    public List<PointsStatisticsVO> queryMyPointsToday() {
        //1.获取当前用户id
        Long userId = UserContext.getUser();

        //2.获取当天开始时间和结束时间
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime begin = DateUtils.getDayStartTime(now);
        LocalDateTime end = DateUtils.getDayEndTime(now);

        //3.查询统计当期获取积分情况
        List<PointsRecord> list = query()
                .select("type", "sum(points) as points")
                .eq("user_id", userId)
                .between("create_time", begin, end)
                .groupBy("type")
                .list();

        //4.组装vo并返回
        List<PointsStatisticsVO> vos = new ArrayList<>(list.size());
        for (PointsRecord pr : list) {
            PointsStatisticsVO vo = new PointsStatisticsVO();
            vo.setType(pr.getType().getDesc());
            vo.setMaxPoints(pr.getType().getMaxPoints());
            vo.setPoints(pr.getPoints());
            vos.add(vo);
        }
        //5.处理没有查询结果的类型
        Set<String> typeSet = vos.stream().map(vo -> vo.getType()).collect(Collectors.toSet());
        List<PointsRecordType> types = Arrays.stream(PointsRecordType.values()).filter(p -> !typeSet.contains(p.getDesc())).collect(Collectors.toList());
        for (PointsRecordType type : types) {
            PointsStatisticsVO vo = new PointsStatisticsVO();
            vo.setType(type.getDesc());
            vo.setMaxPoints(type.getMaxPoints());
            vo.setPoints(0);
        }
        return vos;
    }


    private int queryUserPointsByTypeAndDate(Long userId, PointsRecordType type, LocalDateTime begin,LocalDateTime end){
        LambdaQueryWrapper<PointsRecord> queryWrapper = new LambdaQueryWrapper();
        queryWrapper.eq(PointsRecord::getUserId,userId)
                    .eq(PointsRecord::getType,type)
                    .between(PointsRecord::getCreateTime,begin,end);
      Integer points = getBaseMapper().queryUserPointsByTypeAndDate(queryWrapper);
      return points == null ? 0 : points;
    }

}
